package org.xserver.component.core;

import java.nio.charset.Charset;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.handler.codec.http.Cookie;
import org.jboss.netty.handler.codec.http.CookieEncoder;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.util.CharsetUtil;
import org.xserver.component.handler.ext.WriteHandlerFactory.ResponseType;

/**
 * Enum common Content-Type: Content-Type: [type]/[subtype]; parameter. See
 * http://zh.wikipedia.org/wiki/MIME. Because the HttpServer most time to
 * response json data ,so XServerHttpResponse to client default use test/plain
 * Content-Type. When the business need other type, where should be set in
 * business interface. <code>XServerHttpResponse</code> implement redirect, wrap
 * Header and so an.
 * 
 * <pre>
 * <ul>
 * <li>2014-09-29 Add cookie support</li>
 * </ul>
 * </pre>
 * 
 * <OL>
 * <strong>The login Cookie process as follow:</strong>
 * <LI>User first login to server, server check the account and password, if
 * pass, sign and store info in client cookie and set expire, else return to
 * re-login.
 * <LI>If user view web and request server, client will take cookie to server.
 * And server check the signature, if pass, progress business, else return to
 * re-login.
 * </OL>
 * 
 * @author idoldefine
 * @since 2013/01/20
 */
public class XServerHttpResponse extends DefaultHttpResponse implements
		HttpResponse {
	public enum ContentType {
		/** TYPE: TEXT */
		/** PLAIN TEXT */
		TEXT_PLAIN("text/plain"),
		/** HTML */
		TEXT_HTML("text/html"),

		/** TYPE: IMAGE and VIDEO */
		/** GIF */
		IMAGE_GIF("image/gif"),
		/** JPEG */
		IMAGE_JPEG("image/jpeg"),
		/** PNG */
		IMAGE_PNG("image/png"),
		/** MPEG */
		VIDEO_MPEG("video/mpeg"),

		/** TYPE: APPLICATION */
		/** JSON */
		APPLICATION_JSON("application/json"),
		/** XML */
		APPLICATION_XML("application/xhtml+xml"),
		/** OCTET-STREAM */
		APPLICATION_OCTET_STREAM("application/octet-stream"),
		/** PDF */
		APPLICATION_PDF("application/pdf"),
		/** MICROSOFT WORD */
		APPLICATION_MSWORD("application/msword"),
		/** MICROSOFT EXCEL */
		APPLICATION_MSEXCEL("application/vnd.ms-excel"),
		/** WAP 1.0 */
		APPLICATION_WAP_1_0("application/vnd.wap.xhtml+xml"),
		/** WAP 2.0 */
		APPLICATION_WAP_2_0("application/xhtml+xml"),

		/** RFC 822 */
		MESSAGE_RFC_822("message/rfc822"),
		/** HTML MAIL */
		MULTIPART_ALTERNATIVE("multipart/alternative"),

		/** FORM */
		APPLICATION_FORM("application/x-www-form-urlencoded"),
		/** FORM */
		MULTIPART_FORM_DATA("multipart/form-data");

		private final String contentType;

		ContentType(String contentType) {
			this.contentType = contentType;
		}

		public String getContentType() {
			return contentType;
		}
	}

	private static final Charset DEFAULT_CONTENT_CHARSET = CharsetUtil.UTF_8;

	private Charset contentCharset = DEFAULT_CONTENT_CHARSET;

	public static final String DEFAULT_CHARSET = "charset="
			+ DEFAULT_CONTENT_CHARSET.displayName();

	/** default http version 1.1 */
	public static final HttpVersion DEFAULT_HTTP_VERSION = HttpVersion.HTTP_1_1;
	/** default http response status 200 */
	public static final HttpResponseStatus DEFAULT_HTTP_STATUS = HttpResponseStatus.OK;
	/** response http status */
	private HttpResponseStatus status;

	/** cookie encoder */
	private static final CookieEncoder cookieEncoder = new CookieEncoder(true);

	private int contentLength;

	/**
	 * decided which write handler to response, default:JSON->
	 * {@link TextPlainWriteHandler}
	 */
	private ResponseType responseType = ResponseType.HTTP;

	/** the HTTP response head info: Content-Type, default:text/plain */
	private ContentType contentType = ContentType.TEXT_PLAIN;

	public ContentType getContentType() {
		return contentType;
	}

	public void setContentType(ContentType contentType) {
		this.contentType = contentType;
	}

	public ResponseType getResponseType() {
		return responseType;
	}

	public void setResponseType(ResponseType responseType) {
		this.responseType = responseType;
	}

	public Charset getContentCharset() {
		return contentCharset;
	}

	public void setContentCharset(Charset contentCharset) {
		this.contentCharset = contentCharset;
	}

	public long getContentLength() {
		return contentLength;
	}

	public void setContentLength(int contentLength) {
		this.contentLength = contentLength;
	}

	public HttpResponseStatus getStatus() {
		return status;
	}

	public void setStatus(HttpResponseStatus status) {
		this.status = status;
	}

	public XServerHttpResponse() {
		this(DEFAULT_HTTP_VERSION, DEFAULT_HTTP_STATUS);
	}

	public XServerHttpResponse(HttpVersion version, HttpResponseStatus status) {
		super(version, status);
	}

	public void setContent(ChannelBuffer content) {
		super.setContent(content);
	}

	public void setContentString(String content) {
		setContent(ChannelBuffers.copiedBuffer(content, contentCharset));
	}

	/**
	 * Use default content charset(UTF-8) set response body
	 * 
	 * @param content
	 *            response body content
	 * @return channelbuffer
	 */
	public ChannelBuffer responseBuffer(String content) {
		return responseBuffer(content, DEFAULT_CONTENT_CHARSET);
	}

	/**
	 * Use provided content charset set response body
	 * 
	 * @param content
	 *            response body content
	 * @return channelbuffer
	 */
	public ChannelBuffer responseBuffer(String content, Charset charset) {
		if (charset == null) {
			new IllegalArgumentException(
					"Charset should be setted before convert.");
		}

		return ChannelBuffers.copiedBuffer(content, contentCharset);
	}

	/**
	 * redirect to provided location
	 * 
	 * @param locationURL
	 *            redirect location
	 */
	public void redirect(String locationURL) {
		setStatus(HttpResponseStatus.FOUND);
		setLocationHeader(locationURL);
	}

	public void redirect(HttpResponseStatus status, String locationURL) {
		setStatus(status);
		setLocationHeader(locationURL);
	}

	public void setContentLengthHeader(int length) {
		this.headers().set(Names.CONTENT_LENGTH, length);
	}

	public void setContentTypeHeader(String contentType) {
		this.headers().set(Names.CONTENT_TYPE, contentType);
	}

	public void setConnectionHeader(String connection) {
		this.headers().set(Names.CONNECTION, connection);
	}

	public void setLocationHeader(String location) {
		this.headers().set(Names.LOCATION, location);
	}

	public boolean setHeaderIfEmpty(String name, String value) {
		if (headers().get(name) == null) {
			headers().set(name, value);
			return true;
		}

		return false;
	}

	// *****************************Cookie*****************************
	/**
	 * Add cookie to response
	 * 
	 * @param cookie
	 */
	public void addCookie(Cookie cookie) {
		cookieEncoder.addCookie(cookie);
		headers().add(Names.SET_COOKIE, cookieEncoder.encode());
	}

	/**
	 * Add cookie to response, the cookie will be removed when a user terminates
	 * browser
	 * 
	 * @param cookie
	 */
	public void addCookie(String name, String value) {
		cookieEncoder.addCookie(name, value);
		headers().add(Names.SET_COOKIE, cookieEncoder.encode());
	}

	/**
	 * Sets the max age of this cookie in seconds. If 0 is specified, this
	 * cookie will be removed by browser because it will be expired immediately.
	 * If Integer.MIN_VALUE is specified, this cookie will be removed when a
	 * user terminates browser.
	 * 
	 * The command is exactly equivalent to the following group of commands:
	 * {@link Cookie#setMaxAge(int)} +
	 * {@link XServerHttpResponse#addCookie(Cookie)}
	 * 
	 * @param cookie
	 * @param maxAge
	 *            ttl (unit second)
	 */
	public void setMaxAge(Cookie cookie, int maxAge) {
		cookie.setMaxAge(maxAge);
		addCookie(cookie);
	}
}
